/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#include "gm4lin.h"
#include <time.h>

int open_port(char *port_name) {
/* open a port, knowing its name (for example "/dev/ttyS0") */
/* return file_descriptor or EXIT_FAILURE */
	int file_descriptor; 	/* file descriptor of the port */
	
	/* opening serial port */
	printf(_("\tOpening \"%s\"\n"),port_name);
	/* if O_NDELAY not set: in blocking mode, the process will wait until caracters come in */
	/* if O_NDELAY: non blocking mode, we read the actual value and go on */
	if ((file_descriptor=open(port_name,O_RDONLY|O_NOCTTY|O_NDELAY))==-1) {
		error_log(_("open_port: Unable to open the requested serial port\n"));
		return EXIT_FAILURE;
	}
	
	#ifdef DEBUG
		printf("\tPort %s opened; fd=%d\n",port_name,file_descriptor);
	#endif
	return file_descriptor;
}

int close_port(int file_descriptor) {
/* close a port, knowing its file descriptor */
/* return EXIT_SUCCESS or EXIT_FAILURE */
	
	#ifdef DEBUG
		printf("\tClosing file descriptor %d\n",file_descriptor);
	#endif
	/* closing serial port */
	if (close(file_descriptor)==-1) {
		error_log(_("close_port: Unable to close the requested serial port\n"));
		return EXIT_FAILURE;
	}
	
	#ifdef DEBUG
		printf("\tFile descriptor %d successfully closed\n",file_descriptor);
	#endif
	return EXIT_SUCCESS;
}

int read_port(int file_descriptor, char *data_line,char *iso_time) {
/* read data from a port, knowing its file descriptor */
/* return EXIT_SUCCESS or EXIT_FAILURE */
	struct tm *local_time;		/* local time structure */
	time_t timing;			/* time in seconds since EPOC */
	char *serial_read_buffer;	/* read buffer for serial controler access */
	int number;			/* multi purpose integer */
	
	#ifdef DEBUG
		printf("\tRead function. fd=%d\n",file_descriptor);
	#endif
	/* allocating memory for the serial port read buffer */
	if((serial_read_buffer=(char *)malloc(SERIAL_BYTES_READ_COUNT))== NULL){
		error_log(_("read_port: String memory allocation failure"));
		return EXIT_FAILURE;
	}
		
	/* reading serial port */
	if (read(file_descriptor,serial_read_buffer,(size_t)SERIAL_BYTES_READ_COUNT)==-1) {
		error_log(_("read_port: Unable to read the requested serial port\n"));
		free(serial_read_buffer);
		return EXIT_FAILURE;
	}
	if(time(&timing) == ((time_t)-1)) {
		error_log(_("read_port: Time reading failure - skipping event\n"));
		free(serial_read_buffer);
		return EXIT_FAILURE;
	}

	if((local_time=(struct tm*)localtime(&timing)) != NULL) { /* cast done because gcc was complaining. Why??? */
		strftime(iso_time,(size_t)20,"%Y-%m-%d-%H:%M:%S",(struct tm*)localtime(&timing));
	} else {
		error_log(_("read_port: Time conversion failure"));
	}
	
	number=snprintf(data_line,(size_t)(DATA_LINE_LENGTH*sizeof(char)),"%s %s",iso_time,serial_read_buffer);
	free(serial_read_buffer);
	printf(_("\tRead successfull: %s at %s\n"),serial_read_buffer,iso_time);
	return EXIT_SUCCESS;
}

int clear_port(int file_descriptor) {
/* read data from a port, in order to clear it, knowing its file descriptor */
/* return EXIT_SUCCESS or EXIT_FAILURE */
	char *serial_read_buffer;	/* read buffer for serial controler access */
	
	/* allocating memory for the serial port read buffer */
	if((serial_read_buffer=(char *)malloc(SERIAL_BYTES_READ_COUNT))== NULL){
		error_log(_("clear_port: String memory allocation failure"));
		return EXIT_FAILURE;
	}
		
	/* reading serial port */
	if (read(file_descriptor,serial_read_buffer,(size_t)SERIAL_BYTES_READ_COUNT)==-1) {
		error_log(_("clear_port: Unable to read the requested serial port\n"));
		free(serial_read_buffer);
		return EXIT_FAILURE;
	}
		
	#ifdef DEBUG
		printf("\tData read on port: %s\n",serial_read_buffer);
	#endif
	
	free(serial_read_buffer);
	return EXIT_SUCCESS;
}

int probe_port(int file_descriptor) {
/* check if there is data at a port, knowing its file descriptor */
/* return -1 (failure), 0 (no data), 1 (some data) */
	char *serial_read_buffer;	/* read buffer for serial controler access */
	ssize_t data;
	
	/* allocating memory for the serial port read buffer */
	if((serial_read_buffer=(char *)malloc(SERIAL_BYTES_READ_COUNT))== NULL){
		error_log(_("probe_port: String memory allocation failure"));
		return EXIT_FAILURE;
	}
		
	/* reading serial port */
	data=read(file_descriptor,serial_read_buffer,(size_t)SERIAL_BYTES_READ_COUNT);
	/* pbl: read returns -1 when there is no signal on the port... */
	/*if (data==-1) {
		error_log("probe_port: Unable to read the requested serial port\n");
		free(serial_read_buffer);
		return data;
	}*/
	
	free(serial_read_buffer);
	return (int)data;
}

int set_port(int file_descriptor){
/* set the status flags of a port, knowing its file descriptor */
/* return EXIT_SUCCESS or EXIT_FAILURE */
	struct termios port_options;		/* options for the port */
	tcflag_t cflag;				/* to check that configuration changes are really applied */
	tcflag_t lflag;				/* to check that configuration changes are really applied */
	
	/* reading the current settings of the port */
	if(tcgetattr(file_descriptor,&port_options) == EXIT_FAILURE) {
		error_log(_("set_port: read of current status failed\n"));
		return EXIT_FAILURE;
	}
	
 	/* setting the baud rate for both input and output */
	if((cfsetispeed(&port_options,(speed_t)SERIAL_BAUD_RATE)) == EXIT_FAILURE) {
		error_log(_("set_port: setting of input baud rate failed failed\n"));
		return EXIT_FAILURE;
	}
	if((cfsetospeed(&port_options,(speed_t)SERIAL_BAUD_RATE)) == EXIT_FAILURE) {
		error_log(_("set_port: read of output baud rate failed\n"));
		return EXIT_FAILURE;
	}
	
	/* enabling local mode (ignore control caracters on input) and receiver mode */
	port_options.c_cflag |= (CLOCAL|CREAD);
	/* parity setting */
	switch(SERIAL_PARITY) {
		case 0:	/* no parity */
			port_options.c_cflag &= ~PARENB;
			break;
		case 1:	/* odd parity */
			port_options.c_cflag |= PARENB;
			port_options.c_cflag |= PARODD;
			break;
		case 2:	/* even parity */
			port_options.c_cflag |= PARENB;
			port_options.c_cflag &= ~PARODD;
			break;
	}
	/* stop bit setting */
	if(SERIAL_STOP==1) /* 1 stop bit */
		port_options.c_cflag &= ~CSTOPB;
	else	/* 2 stop bits */
		port_options.c_cflag |= CSTOPB;
	/* data transfert length */
	port_options.c_cflag &= ~CSIZE;			/* masking the character size bit */
	port_options.c_cflag |= SERIAL_WORD_LENGTH;	/* applying the data length */
	/* selecting raw input */
	port_options.c_lflag &= ~(ICANON|ECHO|ECHOE|ISIG);
	
	/* saving these options in order to check that ALL the changes have been applied */
	cflag=port_options.c_cflag;
	lflag=port_options.c_lflag;
	
	/* applying the changes in the control structure port_options to the port */
	/* we don't wait for the port to end current data transfert 		  */
	tcsetattr(file_descriptor,TCSANOW,&port_options);
	
	/* reading (again) the current settings of the port */
	if(tcgetattr(file_descriptor,&port_options) == EXIT_FAILURE) {
		error_log(_("set_port: Read of current status failed\n"));
		return EXIT_FAILURE;
	}
	if((cflag!=port_options.c_cflag) || (lflag!=port_options.c_lflag)) {
		error_log(_("set_port: All the settings have not be applied\n"));
		return EXIT_FAILURE;
	}
	
 	#ifdef DEBUG
		printf("\tSettings of the port successfuly applied for file descriptor %d\n",file_descriptor);
	#endif
	return EXIT_SUCCESS;
}

int dtr_up(int file_descriptor) {
/* make sure the DTR line is asserted in order to provide power to the probe */
/* return EXIT_SUCCESS or EXIT_FAILURE */
/* THIS IS NOT PORTABLE OUTSIDE OF THE UNIX WORLD */
	int port_status;
	
	/* reading the current status */
	if((ioctl(file_descriptor,TIOCMGET,&port_status)) == EXIT_FAILURE) {
		error_log(_("dtr_up: Port status reading failure\n"));
		return EXIT_FAILURE;
	}
	
	/* setting DTR up */
	port_status |= TIOCM_DTR;
	if((ioctl(file_descriptor,TIOCMSET,&port_status)) == EXIT_FAILURE) {
		error_log(_("dtr_up: Port status writing failure\n"));
		return EXIT_FAILURE;
	}
	return EXIT_SUCCESS;
}

int dtr_down(int file_descriptor) {
/* shutdown the dtr line in order to stop powering the probe */
/* return EXIT_SUCCESS or EXIT_FAILURE */
/* THIS IS NOT PORTABLE OUTSIDE OF THE UNIX WORLD */
	int port_status;
	
	/* reading the current status */
	if((ioctl(file_descriptor,TIOCMGET,&port_status)) == EXIT_FAILURE) {
		error_log(_("dtr_down: Port status reading failure\n"));
		return EXIT_FAILURE;
	}
	
	/* setting DTR down */
	port_status &= ~TIOCM_DTR;
	if((ioctl(file_descriptor,TIOCMSET,&port_status)) == EXIT_FAILURE) {
		error_log(_("dtr_down: Port status writing failure\n"));
		return EXIT_FAILURE;
	}
	return EXIT_SUCCESS;
}








